#! /usr/bin/perl

use Carp;
use File::Basename;
use Getopt::Std;
use POSIX;
use Proc::PID::File;
use strict;

our $VERSION = '0.3';

my $banFile = '/net/abuse-ip/ip.txt';
my $pidFile = '/var/run/sshdGuard-client.pid';

my $banLife = 86400 * 3;
my $systemType = undef;
my $tableName = undef;

my %hashBan;

main();

sub callSystem {
    my $str = shift;

    print "${str}\n";
    system $str;
}

sub createPidFile {
    croak 'Already running' if Proc::PID::File->running(dir => dirname($pidFile), name => basename($pidFile));
}

sub expireOldEntries {
    my $now = time;
    foreach (keys %hashBan) {
	delete $hashBan{$_} if ($now - $hashBan{$_} > $banLife);
    }
}

sub getOptions {
    my %opts;

    getopts 'b:l:p:s:t:', \%opts;

    $banFile = $opts{b} if defined $opts{b};
    $banLife = $opts{l} if defined $opts{l};
    $pidFile = $opts{p} if defined $opts{p};
    $systemType = $opts{s} if defined $opts{s};
    $tableName = $opts{t} if defined $opts{t};

    croak 'systemType not defined' unless defined $systemType;
    croak 'tableName not defined' unless defined $tableName;
}

sub main {
    getOptions();

    open B, '-|', "tail -F ${banFile}" or croak $!;
    readBanFile(\*B);

    # Theoretically, it won't reach here
    close B;
    croak 'No way to reach here';
}

sub readBanFile {
    my $fh = shift;

    while (<$fh>) {
	chomp;
	my ($banTime, $ip) = split /\s+/;

	$hashBan{$ip} = $banTime;

	# Expire old entries and update rules
	expireOldEntries();
	updateRules();
    }
}

sub updateRules {
    eval "updateRulesWith${systemType}();";
    carp $@ if $@;
}

sub updateRulesWithIPFW {
    my $fwcmd = '/sbin/ipfw -q';

    foreach (keys %hashBan) {
	callSystem "${fwcmd} table ${tableName} add $_";
    }
}

sub updateRulesWithIPTABLES {
    my $fwcmd = '/sbin/iptables';

    foreach (keys %hashBan) {
	callSystem "${fwcmd} -A ${tableName} -s $_ -j DROP";
    }
}

sub updateRulesWithPF {
    my $fwcmd = '/sbin/pfctl';

    foreach (keys %hashBan) {
	callSystem "${fwcmd} -t ${tableName} -T add $_";
    }
}
